#include "unpifi.h"

void free_ifi_info(struct ifi_info* pifi)
{
  struct ifi_info *ifi, *ifinext;

  for (ifi = pifi; ifi != NULL; ifi = ifinext)
  {
    if (ifi->ifi_addr != NULL)
      free(ifi->ifi_addr);

    if (ifi->ifi_brdaddr != NULL)
      free(ifi->ifi_brdaddr);

    ifinext = ifi->ifi_next;
    free(ifi);
  }
}

struct ifi_info* get_ifi_info(int family, int doaliases)
{
  struct ifi_info *ifi, *ifihead, **ifipnext;
  int sockfd, len, lastlen, flags, myflags, idx = 0, hlen = 0;
  char *ptr, *buf, lastname[IFNAMSIZ], *cptr, *haddr, *sdlname;
  char buff[128] = {'\0'};
  int ifnums = 0;

  struct ifconf ifc;
  struct ifreq *ifr, ifrcopy;
  struct sockaddr_in *sinptr;

  memset(buff,0,sizeof(buff));
  sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  lastlen = 0;
  len = 100 * sizeof(struct ifreq); /* initial buffer size guess */

  for(;;)
  {
    buf = (char*)malloc(len);
    ifc.ifc_len = len;
    ifc.ifc_buf = buf;
    if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0)
    {
	if (errno != EINVAL || lastlen != 0)
	{
	  printf("ioctl error:%s\n",strerror(errno));
	}
    } else
    {
      if (ifc.ifc_len == lastlen)
	break;
      lastlen = ifc.ifc_len;
    }
    len += 10 * sizeof(struct ifreq);
    free(buf);
  }

  ifihead = NULL;
  ifipnext = &ifihead;
  lastname[0] = 0;
  sdlname = NULL;
  ifnums = ifc.ifc_len / sizeof(struct ifreq);
  printf("found %d interface\n",ifnums);

  // ifc_len is the buf len, so increase each one
  for (ptr = buf; ptr < buf + ifc.ifc_len;)
  {
    ifr = (struct ifreq *) ptr;
#ifdef HAVE_SOCKADDR_SA_LEN
    len = max(sizeof(struct sockaddr), ifr->ifr_addr.sa_len);
#else
    switch (ifr->ifr_addr.sa_family)
    {
      case AF_INET6:
	len = sizeof(struct sockaddr_in6);
	break;
      case AF_INET:
      default:
	len = sizeof(struct sockaddr);
	break;
    }
#endif
    ptr += sizeof(struct ifreq);

    if (ifr->ifr_addr.sa_family != family) {
      continue;
    }

    myflags = 0;

    ifrcopy = *ifr;
    ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);
    flags = ifrcopy.ifr_flags;
    if ((flags & IFF_UP) == 0) {
      continue; /* ignore if interface not up */
    }

    ifi = (struct ifi_info*)calloc(1,sizeof(struct ifi_info));
    *ifipnext = ifi;
    ifipnext = &ifi->ifi_next;
    ifi->ifi_flags = flags;
    ifi->ifi_myflags = myflags;
    ioctl(sockfd,SIOCGIFMTU,&ifrcopy);
    ifi->ifi_mtu = ifrcopy.ifr_mtu;
    memcpy(ifi->ifi_name,ifr->ifr_name,IFI_NAME);
    ifi->ifi_name[IFI_NAME-1] = '\0';

    switch (ifr->ifr_addr.sa_family)
    {
      case AF_INET:
      default:
	sinptr = (struct sockaddr_in *) &ifr->ifr_addr;
	ifi->ifi_addr = (sockaddr_in*)calloc(1, sizeof(struct sockaddr_in));
	memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));

        if (flags & IFF_BROADCAST)
        {
          ioctl(sockfd,SIOCGIFBRDADDR,&ifrcopy);
          sinptr = (sockaddr_in*)&ifrcopy.ifr_broadaddr;
          ifi->ifi_brdaddr = (sockaddr_in*)calloc(1,sizeof(sockaddr_in));
          memcpy(ifi->ifi_brdaddr,sinptr,sizeof(sockaddr_in));
        }
	break;
    }

  }

  free(buf);
  return ifihead;
}
